#include "DetectorConstruction.hh"

#include "DetectorMessenger.hh"
#include "G4Box.hh"
#include "G4Colour.hh"
#include "G4GeometryManager.hh"
#include "G4LogicalVolume.hh"
#include "G4LogicalVolumeStore.hh"
#include "G4Material.hh"
#include "G4MaterialTable.hh"
#include "G4NistManager.hh"
#include "G4PVPlacement.hh"
#include "G4PhysicalVolumeStore.hh"
#include "G4RunManager.hh"
#include "G4SolidStore.hh"
#include "G4SystemOfUnits.hh"
#include "G4VPhysicalVolume.hh"
#include "G4VisAttributes.hh"
#include "G4ios.hh"
#include "MyMacros.hh"
// #include "G4Element.hh"
// #include "G4ElementTable.hh"
// #include "G4PhysicalConstants.hh"
// #include "G4SDManager.hh"
// #include "G4Tubs.hh"
// #include "globals.hh"

DetectorConstruction::DetectorConstruction()
{
    DINFOLN("[DetectorConstruction] DetectorConstruction()");
    _fMessenger = new DetectorMessenger(this);
    _fVerbose = false;

    G4NistManager* man = G4NistManager::Instance();
    _fAbsorberMaterial = man->FindOrBuildMaterial("G4_WATER");
    _fWorldMaterial = man->FindOrBuildMaterial("G4_Galactic");
    G4cout << *(G4Material::GetMaterialTable()) << G4endl;

    _fPhantomXY = 102.0 * mm;
    _fPhantomZ = 400.0 * mm;
    _fFactor = 1.2;  //2022.10.18
}

DetectorConstruction::~DetectorConstruction() { delete _fMessenger; }

void DetectorConstruction::InitialiseGeometryParameters()
{
    // Volume sizes
    _fWorldXY = _fFactor * _fPhantomXY;
    _fWorldZ = _fFactor * _fPhantomZ;

    _fPhantomShiftZ = -0.5 * _fPhantomZ;
    _fGeneratorPosZ = _fPhantomShiftZ;

    DumpGeometryParameters();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

G4VPhysicalVolume* DetectorConstruction::Construct()
{
    DINFOLN("[DetectorConstruction] Construct");

    InitialiseGeometryParameters();

    G4GeometryManager::GetInstance()->OpenGeometry();
    G4PhysicalVolumeStore::GetInstance()->Clean();
    G4LogicalVolumeStore::GetInstance()->Clean();
    G4SolidStore::GetInstance()->Clean();

    ///==================================== World
    // raw geo
    constexpr G4double iHalf = 2.0;
    G4Box* solidWorld = new G4Box("World", _fWorldXY / iHalf, _fWorldXY / iHalf, _fWorldZ / iHalf);
    // geo with material -> logical geo
    G4LogicalVolume* logicWorld = new G4LogicalVolume(solidWorld, _fWorldMaterial, "World");
    // geo with shift -> physical geo
    G4VPhysicalVolume* physWorld = new G4PVPlacement(0, G4ThreeVector(), "World", logicWorld, 0, false, 0);

    ///==================================== Phantom Box
    G4Box* solidPhantom = new G4Box("Phantom", _fPhantomXY / iHalf, _fPhantomXY / iHalf, _fPhantomZ / iHalf);
    _fLogicPhantom = new G4LogicalVolume(solidPhantom, _fAbsorberMaterial, "Phantom");
    _fPhantom = new G4PVPlacement(0, G4ThreeVector(), "Phantom", _fLogicPhantom, physWorld, false, 0);

    ///==================================== Visualization attributes
    G4VisAttributes* VisAtt = 0;
    VisAtt = new G4VisAttributes(G4Colour(1.0, 1.0, 0.0));
    VisAtt->SetVisibility(true);
    _fLogicPhantom->SetVisAttributes(VisAtt);

    VisAtt = new G4VisAttributes(G4Colour(0.0, 1.0, 1.0));
    VisAtt->SetVisibility(true);
    logicWorld->SetVisAttributes(VisAtt);

    return physWorld;
}

void DetectorConstruction::SetNewMaterial(const G4String& mat)
{
    /// search the material by its name
    G4Material* pttoMaterial = G4NistManager::Instance()->FindOrBuildMaterial(mat);
    if (!pttoMaterial)
    {
        G4cout << "Material " << mat << " is not found out!" << G4endl;
    }
    else if (pttoMaterial != _fAbsorberMaterial)
    {
        _fAbsorberMaterial = pttoMaterial;
        G4cout << "New phantom material " << mat << G4endl;
        if (_fLogicPhantom)
        {
            _fLogicPhantom->SetMaterial(_fAbsorberMaterial);
        }
        G4RunManager::GetRunManager()->PhysicsHasBeenModified();
    }
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void DetectorConstruction::DumpGeometryParameters()
{
    LINFO(
        "````````````````````````[DetectorConstruction/DumpGeometryParameters]\n"
        "World width(mm)= {},\n"
        "Phantom width(mm)= {}, PhantomXY(mm)= {}, Phantom= {}",
        _fWorldZ / mm,                                                    //
        _fPhantomZ / mm, _fPhantomXY / mm, _fAbsorberMaterial->GetName()  //
    )
}
